grammar plp3;

@header
{
	import java.lang.String;
}

@members
{
	TablaSimbolos tablaSimbolos = new TablaSimbolos();
	final String initHeader = "/* plp.il */ \n\n.assembly extern mscorlib {}\n.assembly 'plp' {} \n\n\n";
	int nivel = 0;
}

@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch 
{ 
	catch (RecognitionException re)
	{ 
		reportError(re); 
		System.exit(1); 
	} 
} 


/* SINTAX READER */
s :	
	clase
	{
		System.out.println( initHeader + $clase.trad);
	};

clase
returns [ String trad ]
@init {StringBuilder sb = new StringBuilder(); trad = "";} :  
	CLASS
	SINGLE
	{
		SimboloClase s = new SimboloClase("clase", new TipoClase(null));
		tablaSimbolos.addSimbolo(s);
	}
	LLAVEI
	{
		tablaSimbolos = tablaSimbolos.push();
		nivel++;
	}
	metodo
	{
		sb.append(tablaSimbolos.toString());
		sb.append($metodo.trad);
		sb.append("}\n");
		$trad = sb.toString();
	}
	LLAVED;

metodo
returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :  
	PUBLIC
	STATIC
	VOID
	MAIN
	PARI
	PARD
	bloque
	{
		SimboloFuncion s = new SimboloFuncion("main", null);
		tablaSimbolos.addSimbolo(s);
		salida.append(tablaSimbolos.toString(""));
		salida.append($bloque.trad).append("").append("}\n");
		$trad = salida.toString();
		tablaSimbolos = tablaSimbolos.pop();
	};

tipoSimple returns [ Tipo tipo ]: 
	INT 
	{
		tipo = new TipoEntero();
	} | 
	DOUBLE
	{
		tipo = new TipoReal();
	} |
	BOOL
	{
		tipo = new TipoBooleano();
	};

decl returns [ String trad ]
@init {trad = "";} :
	constdecl 
	{
		$trad = $constdecl.trad;
	}
	|
	vardecl
	{
		$trad = $vardecl.trad;
	};

constdecl returns [ String trad ]
@init {trad = "";} :
	CONST
	tipoSimple
	constid[$tipoSimple.tipo]
	(
		COMA
		constid[$tipoSimple.tipo]
	)*
	PYC;

constid[Tipo tipo] :
	ID
	{
		Simbolo s = new Simbolo($ID.text, tipo );
		s.setConstante();
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			//TODO lanzar error
		}
	}
	ASIG
	(
		ENTERO 
		{
			if ( !tipo.isEntero() )
			{
				//TODO error
			}
		}
		|
		REAL
		{
			if ( !tipo.isReal() )
			{
				//TODO error
			}
		}
		|
		BOOLEANO
		{
			if ( !tipo.isBoolean() )
			{
				//TODO error
			}
		}
	);

vardecl returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	tipoSimple
	varid[$tipoSimple.tipo]
	{
		
	}
	(
		COMA
		varid[$tipoSimple.tipo]
	)*
	PYC
	{
		$trad = tablaSimbolos.toString();
	};

varid[Tipo tipo]
@init {Tipo tipoFinal;} :
	ID
	{
		Simbolo s = new Simbolo($ID.text, tipo);
	}
	(
		CORI
		{
			tipoFinal = new TipoArray(tipo, null);
			s.setTipo(tipoFinal);
		}
		(
			{
				TipoArray t = new TipoArray(tipoFinal, null);
				tipoFinal = t;
				s.setTipo(tipoFinal);
			}
			COMA
		)*
		CORD
	)?
	{
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			//TODO
		}
	};

declins returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	(
		instr
		{
			salida.append($instr.trad);
		}
		|
		decl
		{
			salida.append($decl.trad);
		}
	)*
	{
		$trad = salida.toString();
	};

bloque
returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	LLAVEI
	{
		tablaSimbolos = tablaSimbolos.push();
	}
	declins
	{
		$trad = $declins.trad;
	}
	LLAVED
	{
		tablaSimbolos = tablaSimbolos.pop();
	};

instr returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	bloque
	{
		salida.append($bloque.trad);
	}
	| 
		IF PARI expr PARD instr ( ELSE instr )?
	|
		WHILE PARI expr PARD instr
	|
		FOR PARI INT ID ASIG expr TO expr STEP (ADDOP)? ENTERO PARD instr
	|
		BREAK PYC
	|
		CONTINUE PYC
	|
		ref cambio
	|
		ID ASIG NEW tipoSimple CORI dims CORD PYC
	|
		WRITELINE
		PARI
		{
			salida.append("call void [mscorlib]System.Console::WriteLine(");
		}
		expr
		{
			salida.append($expr.cima).append(")");
		}
		PARD
		PYC
	|
		TRY bloque bloquecatch
	|
		THROW ( NEW EXCEPTION PARI expr PARD )? PYC
	| 
		EXPLAIN ID;

bloquecatch : CATCH PARI EXCEPTION ID PARD bloque;

dims : ENTERO (COMA ENTERO)*;

cambio :
	ASIG expr PYC |
	PUNTO READLINE PYC;

expr returns [ String cima ]
@init {StringBuilder salida = new StringBuilder(); cima = "";} :
	eand
	(
		OR
		eand
	)*;

eand : erel ( AND erel )*;

erel : esum ( RELOP esum )*;

esum : term ( ADDOP term )*;

term : factor ( MULOP factor )*;

factor :
	base |
	NOT factor |
	PARI ADDOP factor PARD;

base :
	( ENTERO | REAL | BOOLEANO ) |
	PARI expr PARD | 
	ref;

ref : ID (CORI indices CORD)?;

indices : expr (COMA expr)* ;

/* Analizador léxico: */ 
CLASS		:	'class';
SINGLE		:	'Single';
VOID		:	'void';
MAIN		:	'Main';
INT			:	'int';
DOUBLE		:	'double';
BOOL		:	'bool';
PUBLIC		:	'public';
STATIC		:	'static';
IF			:	'if';
ELSE		:	'else';
FOR			:	'for';
TO			:	'to';
STEP		:	'step';
WHILE		:	'while';
BREAK		:	'break';
CONTINUE	:	'continue';
NEW			:	'new';
WRITELINE	:	'System.Console.WriteLine';
READLINE	:	('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
LLAVEI		:	'{';
LLAVED		:	'}';
PARI		:	'(';
PARD		:	')';
CORI		:	'[';
CORD		:	']';
COMA		:	',';
PYC			:	';';
ASIG		:	'=';
OR			:	'|';
AND			:	'&';
RELOP		:	'=='|'!='|'<' | '>' | '<=' | '>=';
ADDOP		:	'+' | '-';
MULOP		:	'*' | '/';
NOT			:	'!';
PUNTO		:	'.';
ENTERO		:	('0'..'9')+;
REAL		:	('0'..'9')+'.'('0'..'9')+;
BOOLEANO	:	'True' | 'False';
CONST		:	'const';
EXPLAIN		:	'explain';
TRY			:	'try';
CATCH		:	'catch';
THROW		:	'throw';
EXCEPTION	:	'Exception';
ID			:	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
NEWLINE		:	('\r'? '\n'|' '|'\t')+  {skip();}; 
COMENTARIO	:	'/*' .* '*/' {skip();};
COMENLINEA	:	'//' .* ('/r' | '/n')  {skip();};
